/*
为用户提供 -- Rpc请求的发送，同时获取Rpc请求的结果
*/

#pragma once

#include "Requestor.hpp"

namespace RpcModule
{
    namespace ClientModule
    {
        class RpcCaller
        {
        public:
            using ptr = std::shared_ptr<RpcCaller>;
            using JsonAsyncResponse = std::future<Json::Value>;
            using JsonCallBackResponse = std::function<void(const Json::Value &)>;
            RpcCaller(const Requestor::ptr& requestor): _requestor(requestor) {}
            // 1. 同步策略
            // 传入要调用的方法，以及方法参数，当服务端处理完进行响应时
            // 需要阻塞等待结果，结果从result返回
            bool Call(const BaseConnection::ptr &conn, const std::string &method,
                      const Json::Value &params, Json::Value &result)
            {
                LOGDEBUG("开始同步Rpc调用");
                // 1. 组织请求
                RpcRequest::ptr request = MessageFactory::CreateMessage<RpcRequest>();
                request->SetId(UUID::GetUuid());
                request->SetMType(MType::REQ_RPC);
                request->SetRpcMethod(method);
                request->SetRpcParams(params);
                // 2. 发送请求
                BaseMessage::ptr response_result;
                bool ret = _requestor->Send(conn, std::dynamic_pointer_cast<BaseMessage>(request), response_result);
                if (ret == false)
                {
                    LOGERROR("同步Rpc调用失败");
                    return false;
                }
                // 3. 等待结果
                RpcResponse::ptr response = std::dynamic_pointer_cast<RpcResponse>(response_result);
                if (!response)
                {
                    LOGERROR("Rpc请求向下转换失败！");
                    return false;
                }
                if (response->GetRcode() != RCode::RCODE_OK)
                {
                    LOGERROR("rpc同步请求出错，%s", ErrReason(response->GetRcode()).c_str());
                    return false;
                }
                result = response->GetResult();
                LOGDEBUG("Rpc同步调用完毕，结果设置完毕");
                return true;
            }
            // 2. 异步策略 -- 需要有一个future对象进行结果的接收
            // 对用户来说，返回的结果应该是一个Json对象
            // 传入要调用的方法，以及方法参数，当服务端处理完进行响应时
            // 会自动设置结果
            bool Call(const BaseConnection::ptr &conn, const std::string &method,
                      const Json::Value &params, JsonAsyncResponse &result)
            {
                // 1. 组织请求
                RpcRequest::ptr request = MessageFactory::CreateMessage<RpcRequest>();
                request->SetId(UUID::GetUuid());
                request->SetMType(MType::REQ_RPC);
                request->SetRpcMethod(method);
                request->SetRpcParams(params);
                // 2. 这里是异步获取结果，当用户想要结果时，可以使用传入的future直接进行获取
                //      但是对于结果的获取，是必须要等待服务端进行回应之后，才可以进行消息的获取的，所以这里采取回调函数
                //      服务端向客户端做出回应之后，调用回调函数，回调函数中才进行结果的设置，防止阻塞
                // 因为json_promise是临时对象，如果用户暂时不需要结果，那么函数结束之后，json_promise释放
                // result就会抛出异常，对于智能指针，会因为bind的绑定，智能指针的引用计数+1
                // 即使函数结束，智能指针的引用计数也不会为0，所以也不会释放！
                auto json_promise = std::make_shared<std::promise<Json::Value>>();
                result = json_promise->get_future();
                Requestor::RequestCallBack cb = std::bind(&RpcCaller::CallBackAsync, this, json_promise, std::placeholders::_1);
                bool ret = _requestor->Send(conn, std::dynamic_pointer_cast<BaseMessage>(request), cb);
                if (ret == false)
                {
                    LOGERROR("异步Rpc请求失败！");
                    return false;
                }
                return true;
            }
            // 3. 回调函数策略 -- 需要有一个回调函数
            //      对用户来说，回调函数处理的应该是一个Json对象
            // 传入要调用的方法，以及方法参数，当服务端处理完进行响应时
            // 会自动调用cb回调函数
            bool Call(const BaseConnection::ptr &conn, const std::string &method,
                      const Json::Value &params, const JsonCallBackResponse &cb)
            {
                // 1. 组织请求
                RpcRequest::ptr request = MessageFactory::CreateMessage<RpcRequest>();
                request->SetId(UUID::GetUuid());
                request->SetMType(MType::REQ_RPC);
                request->SetRpcMethod(method);
                request->SetRpcParams(params);
                // 2. 设置回调函数
                Requestor::RequestCallBack req_cb = std::bind(&RpcCaller::CallBack, this, cb, std::placeholders::_1);
                bool ret = _requestor->Send(conn, std::dynamic_pointer_cast<BaseMessage>(request), req_cb);
                if (ret == false)
                {
                    LOGERROR("回调Rpc请求失败！");
                    return false;
                }
                return true;
            }

        private:
            void CallBackAsync(std::shared_ptr<std::promise<Json::Value>> result, const BaseMessage::ptr &message)
            {
                // 只有当服务端做出相应之后，客户端才运行回调函数，设置结果
                // 1. 获取服务端的响应
                RpcResponse::ptr response = std::dynamic_pointer_cast<RpcResponse>(message);
                if (!response)
                {
                    LOGERROR("Rpc请求向下转换失败！");
                    return;
                }
                if (response->GetRcode() != RCode::RCODE_OK)
                {
                    LOGERROR("rpc异步请求出错，%s", ErrReason(response->GetRcode()).c_str());
                    return;
                }
                result->set_value(response->GetResult());
            }
            void CallBack(const JsonCallBackResponse &callback, const BaseMessage::ptr &message)
            {
                // 1. 获取服务端的响应
                auto response = std::dynamic_pointer_cast<RpcResponse>(message);
                if (!response)
                {
                    LOGERROR("rpc响应，向下类型转换失败！");
                    return;
                }
                if (response->GetRcode() != RCode::RCODE_OK)
                {
                    LOGERROR("rpc异步请求出错，%s", ErrReason(response->GetRcode()).c_str());
                    return;
                }
                // 2. 当服务端进行响应时，在这里进行的是结果的处理
                callback(response->GetResult());
            }

        private:
            // 1. 客户端任何请求的发送，都需要经过Requestor
            Requestor::ptr _requestor;
        };
    }
}